﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace pictureAnalyse
{
    /// <summary>
    /// 定义一个统计值点：索引值、数据值
    /// </summary>
    public class ValuePoint
    {
        /// <summary>
        /// 索引值
        /// </summary>
        public float Site = -1;

        /// <summary>
        /// 数据值
        /// </summary>
        public float Value = -1;

        /// <summary>
        /// 构建一个统计值点
        /// </summary>
        public ValuePoint(){}

        /// <summary>
        /// 构建一个统计值点
        /// </summary>
        public ValuePoint(float site, float value)
        {
            Site = site;
            Value = value;
        }

        /// <summary>
        /// 创建当前统计值点的一个副本
        /// </summary>
        public ValuePoint copy()
        {
            return new ValuePoint(this.Site, this.Value);
        }
    }

    /// <summary>
    /// 统计值分组信息：定义两个波谷最低点之间包含一个波峰最高点为一个分组
    /// 使用6点，定义一个波峰，从左到右依次为 S_、S、M、M_、E、E_
    /// </summary>
    public class ValueGroup
    {
        public ValuePoint Start, Max, End;      // 分别表示分组的：起点、峰值点、终点
        public ValuePoint Start_, Max_, End_;   // 分别表示分组的：起点之前的起点等值点、峰值之后的峰值等值点、终点之后的终点等值点
        public bool confirmStart = false, confirmMax = false, confirmEnd = false;  // 记录起点、峰值点、终点，是否均已确定

        /// <summary>
        /// 构建一个统计值分组
        /// </summary>
        public ValueGroup()
        {
            Start = new ValuePoint();
            Max = new ValuePoint();
            End = new ValuePoint();
            Start_ = new ValuePoint();
            Max_ = new ValuePoint();
            End_ = new ValuePoint();
        }

        /// <summary>
        /// 构建一个统计值分组
        /// </summary>
        public ValueGroup(ValuePoint P)
        {
            Start = P.copy();
            Max = P.copy();
            End = P.copy();
            Start_ = P.copy();
            Max_ = P.copy();
            End_ = P.copy();
        }

        /// <summary>
        /// 获取当前分组的面积大小，定义分组的面积为5段S_、S、M、M_、E、E_
        /// </summary>
        public float getArea()
        {
            float AreaS_S = getArea(Start_.Value, Start.Value, Start.Site - Start_.Site) / 2;
            float AreaSM = getArea(Start.Value, Max.Value, Max.Site - Start.Site);
            float AreaMM_ = (Max_.Site - Max.Site) * Max.Value;
            float AreaM_E = getArea(Max_.Value, End.Value, End.Site - Max_.Site);
            float AreaEE_ = getArea(End.Value, End_.Value, End_.Site - End.Site) / 2;

            float area = AreaS_S + AreaSM + AreaMM_ + AreaM_E + AreaEE_;
            return area;
        }

        /// <summary>
        /// 获取梯形的面积
        /// </summary>
        private float getArea(float U, float B, float H)
        {
            return (U + B) * H / 2;
        }

        /// <summary>
        /// 获取当前分组起点的中点
        /// </summary>
        /// <returns></returns>
        public float getS()
        {
            return (Start_.Site + Start.Site) / 2;
        }

        /// <summary>
        /// 获取当前分组峰值点的中点
        /// </summary>
        public float getM()
        {
            return (Max.Site + Max_.Site) / 2;
        }

        /// <summary>
        /// 获取当前分组终点的中点
        /// </summary>
        public float getE()
        {
            return (End.Site + End_.Site) / 2;
        }

        /// <summary>
        /// 获取当前分组的字符串形式，输出显示当前分组的概要信息
        /// </summary>
        public String ToString()
        {
            String Str = "分组";
            Str += "【" + Start.Site+ ", " + End.Site + "】 ";
            Str += "Max(" + getM() + ", " + Max.Value + ") ";
            Str += "面积:" + getArea();
            return Str;
        }

        /// <summary>
        /// 判断group分组范围内是否包含site表示的颜色值
        /// </summary>
        public bool Contains(float site)
        {
            return getS() <= site && site <= getE();
        }

        /// <summary>
        /// 判断groups分组范围内是否包含site表示的颜色值
        /// </summary>
        public static bool Contains(List<ValueGroup> groups, float site)
        {
            bool contain = false;
            foreach(ValueGroup group in groups)
            {
                if(group.Contains(site))
                {
                    contain = true;
                    break;
                }
            }
            return contain;
        }
    }

    class ValueAnalyser
    {
        #region 颜色值统计信息渐变处理

        ///// <summary>
        ///// 对颜色统计值进行渐变处理，取V(i) = [V(i-1) + V(i) + V(i+1)] / 3
        ///// </summary>
        //public float[] arverageProcess(float[] V)
        //{
        //    float[] F = new float[V.Length];

        //    //float S = 0, E = 0;
        //    //for (int i = 0; i < V.Length; i++)
        //    //{
        //    //    S = (i - 1 >= 0) ? V[i - 1] : 0;        // V(i-1)
        //    //    E = (i + 1 < V.Length) ? V[i + 1] : 0;  // V(i+1)
        //    //    F[i] = (S + V[i] + E) / 3;              // 取均值
        //    //}

        //    return F;
        //}

        /// <summary>
        /// 对V进行临近均值计算
        /// </summary>
        public static float[] getAverage(float[] V)
        {
            int range = getMaxRange(V.Length);      // 获取最大均值半径
            float[] F = new float[V.Length];

            for (int R = 1; R <= range; R++)        // 均值半径从1到maxR
            {
                F = new float[V.Length];
                for (int i = 0; i < V.Length; i++)  // 对所有值分别计算临近均值
                {
                    F[i] = Arverage(V, i, R);       // 计算第i项的R半径范围内的均值
                }
                V = F;
            }
            return F;
        }

        /// <summary>
        /// 获取相对于len的最大均值半径
        /// </summary>
        private static int getMaxRange(int len)
        {
            int R = 3, i = 1;
            while (R < len / 2)
            {
                R *= 2;
                i++;
            }
            return i;
        }

        /// <summary>
        /// 获取index项前后range项范围内的均值
        /// </summary>
        private static float Arverage(float[] V, int index, int range)
        {
            float sum = I(V, index);
            for (int i = 1; i <= range; i++)
            {
                sum += I(V, index - i);
                sum += I(V, index + i);
            }
            return sum / (range * 2 + 1);   // 取均值
        }

        /// <summary>
        /// 获取V的第index项值
        /// </summary>
        private static float I(float[] V, int index)
        {
            if (V == null || V.Length == 0 || index < 0 || index >= V.Length) return 0;
            else return V[index];
        }

        #endregion

        # region 对连续起伏的统计信息自动进行分组（要求输入的信息，已进行了渐变处理，当前为连续起伏的统计值）

        /// <summary>
        /// 根据统计值信息，从第index个统计值开始，自动获取生成第一个分组
        /// </summary>
        public static ValueGroup getGroup(float[] V, int index)
        {
            ValueGroup group = null;
            if (V == null || index < 0) return group;

            for (int i = index; i < V.Length; i++)
            {
                ValuePoint P = new ValuePoint(i, V[i]);     // 获取第index个统计值点
                if (group == null) group = new ValueGroup(P);   // 从统计值点，创建一个新的分组
                else
                {
                    // 确定起点
                    if (!group.confirmStart)
                    {
                        // 对于统计点P[i]
                        if (P.Value < group.Start.Value)            // 其值小于波谷起点值，则定义它为起点
                        {
                            group.Start = P.copy();
                            group.Start_ = P.copy();
                        }
                        else if (P.Value == group.Start.Value)     // 若等于波谷起点值，则向后延伸起点值位置
                        {
                            group.Start = P.copy();
                        }
                        else
                        {
                            group.confirmStart = true;             // 若大于波谷值，则起点位置确定

                            group.Max = P.copy();                  // 设置波峰起始点
                            group.Max_ = P.copy();
                        }
                    }

                    // 确定波峰
                    else if (group.confirmStart && !group.confirmMax)
                    {
                        if (P.Value > group.Max.Value)              // 若大于波峰值，则定义为波峰起点位置
                        {
                            group.Max = P.copy();
                            group.Max_ = P.copy();
                        }
                        else if (P.Value == group.Max.Value)        // 若等于波峰值，则向后延伸波峰终点位置
                        {
                            group.Max_ = P.copy();
                        }
                        else
                        {
                            group.confirmMax = true;                // 若小于波峰值，则波峰位置确定

                            group.End = P.copy();                   // 定义终点起始位置
                            group.End_ = P.copy();
                        }
                    }

                    // 确定终点
                    else if (group.confirmMax && !group.confirmEnd)
                    {
                        if (P.Value < group.End.Value)                      // 若值小于终点值，则定义为终点位置
                        {
                            group.End = P.copy();
                            group.End_ = P.copy();
                        }
                        else if (P.Value == group.End.Value)                // 若值等于重点值，则向后延伸终点结束位置
                        {
                            group.End_ = P.copy();
                        }
                        else
                        {
                            group.confirmEnd = true;
                            break;
                        }
                    }
                }
            }

            if (!group.confirmStart || !group.confirmMax) group = null; //若起点或波峰未确定，则获取分组失败

            return group;
        }

        /// <summary>
        /// 根据统计值信息，自动获取所有分组
        /// </summary>
        public static List<ValueGroup> getGroups(float[] V)
        {
            List<ValueGroup> groups = new List<ValueGroup>();

            for (int i = 0; i < V.Length; i++)
            {
                ValueGroup group = getGroup(V, i);  // 从索引位置i开始获取分组
                if (group != null)
                {
                    groups.Add(group);              // 记录获取的分组信息
                    i = (int) group.End.Site;       // 以当前分组的终点索引，作为新分组的起点
                }
            }

            return groups;
        }

        /// <summary>
        /// 获取所有分组信息
        /// </summary>
        public static String ToString(List<ValueGroup> groups)
        {
            String Str = "";
            foreach (ValueGroup group in groups)
                Str += group.ToString() + "\r\n";
            return Str;
        }

        # endregion
    }
}
